////////////////////////////////////////////////
// shader constants

float4 GlowParams           : PB_GlowParams;
float4 TexTileSize          : PI_TextureTileSize;	// xy = tile width/height, z = tile count, w = start
float4 MotionBlurInfo       : PI_MotionBlurInfo; // x = cam stretch, y = movement stretch, z = blurring
float4 ParticleParams       : PI_ParticleParams; // per emitter parameters ( x = alpha scale, y = diffuse lighting, z = back lighting, w = curved normals )

float4 CameraRightVector		: PB_CameraRight;
float4 CameraUpVector				: PB_CameraUp;
float4 CameraFrontVector		: PB_CameraFront;

float4 TimeParams = { PB_FrameTime 0.01, PB_FrameTime 0.05, PB_FrameTime 0.1, PB_FrameTime 0.2 };
float4x4 ViewProjMatrPrev : PB_ViewProjMatrixPrev;

#define HALFRES_DEBUG_COLOR half3(0.3h, 3.h, 0.3h)

struct app2vertParticleGeneral
{
  float4 Position  : POSITION; //in object space  
  float4 Color     : COLOR0;
  float4 baseTC    : COLOR1;
  float3 XAxis     : TEXCOORD0;
  float3 YAxis     : TEXCOORD1;
#if %_RT_MOTION_BLUR
	float3 PrevPos   : POSITION1;
	float3 PrevXAxis : TEXCOORD2;
	float3 PrevYAxis : TEXCOORD3;
#endif
};

// add other required stuff here
struct vert2FragParticleGeneral
{
  float4 Position  : POSITION;  //in projection space
  
#if %_RT_MOTION_BLUR
	half4 SSCenterMotion : TEXCOORDN;
	half4 MotionBlur : TEXCOORDN;
	half3 MotionBlur2 : TEXCOORDN;
#endif
};

void Particle_Setup_General(inout float4 vPos, float4x4 mViewProj, inout app2vertParticleGeneral IN, inout vert2FragParticleGeneral OUT)
{
	//////////////////////////////////////////////////////
  // coord expansion
	const half2 vLocalCoords = IN.baseTC.xy * 2 - 1;

	// Apply TCs to axes
	vPos.xyz += IN.XAxis * vLocalCoords.x + IN.YAxis * (vLocalCoords.y * IN.baseTC.w);
  OUT.Position = mul(mViewProj, vPos);

#if %_RT_MOTION_BLUR
	const half2 vStrength = TimeParams.yy * MotionBlurInfo.xy;
	const half4 vScreenRot = IN.ScreenRot;
	float4 vCenterPos = float4(vPos.xyz, 1);
	float4 vPrevPos = Particle_Coord_Expand(IN.PrevPos, tangentSpace, vLocalCoords, IN.PrevScreenRot);
	vPrevPos = mul(mViewProj, vPrevPos);

	// screenspace positions
	float4 vCurrPos = OUT.Position;
	const float2 vSSCenterPos = OUT.SSCenterMotion.xy; // center of particle shape in current frame
	const float2 vSSPrevPos = vPrevPos.xy / vPrevPos.w;
	const float2 vSSCurrPos = vCurrPos.xy / vCurrPos.w;
	// motion vector in SS (camera, vertex)
	const half2 vSSVertMotionVec = vStrength.y * (vSSCurrPos - vSSPrevPos);
	float2 vSSMotionVec = vStrength.x * OUT.SSCenterMotion.zw + vSSVertMotionVec;
	// limit distance
	half fOpacityCoef = 1;
	half fBlendRatio = 1;
	half2 vUVGrad = 0;
	const half2 vSSRadius = vSSCurrPos - vSSCenterPos;
	half fConserveRatio = 1.h;
	const half2 vVertPosLocal = normalize(vSSRadius);
	float fSSMotionLength = length(vSSMotionVec);
	if (fSSMotionLength > 1e-4h)
	{
		const half2 vSSMotionDir = vSSMotionVec / fSSMotionLength;
		fSSMotionLength = min(fSSMotionLength, 0.5f);
		fSSMotionLength *= fSSMotionLength;
		vSSMotionVec = vSSMotionDir * fSSMotionLength;
		// ratio for blending between current and previous position based on motion vector and vertex position in particle shape
		const half fPrevCurrBlend = dot(vVertPosLocal, vSSMotionDir);
		fBlendRatio = 0.5h + 0.5h * fPrevCurrBlend;

		// extend stretch
		static const half fFwdBwdRatio = 0.75h;
		vPrevPos.xy = vSSPrevPos - vSSMotionVec * fFwdBwdRatio;
		vCurrPos.xy = vSSCurrPos + vSSMotionVec * (1.h - fFwdBwdRatio);
		vPrevPos.xy *= vPrevPos.w;
		vCurrPos.xy *= vCurrPos.w;

#if !%REFRACTION
		vUVGrad.x = dot(normalize(vScreenRot.xy), vSSMotionDir);
		vUVGrad.y = dot(normalize(vScreenRot.zw), vSSMotionDir);
		vUVGrad.xy *= vUVGrad.xy * fSSMotionLength * MotionBlurInfo.z;
#endif

		// Rough estimate of screen space area change of a particle
		const half fConserveMin = 0.4h - saturate(0.1h * (GlowParams.x + Ambient.x)) * 0.35h;
		fConserveRatio = max(saturate(length(vSSRadius) / fSSMotionLength), fConserveMin);
	}

	OUT.MotionBlur = half4(vSSMotionVec, vVertPosLocal);
	OUT.MotionBlur2 = half3(min(abs(vUVGrad), 0.04h * TexTileSize.xy), fConserveRatio);

	// lerp between current and previous frame position
  OUT.Position = lerp(vPrevPos, vCurrPos, fBlendRatio);
#endif
}

//========================================================================
